package com.tl.branchmanagementdemo.Interceptor;

import com.tl.branchmanagementdemo.Interface.StatementModifier;
import com.tl.branchmanagementdemo.service.impl.IdentitySelectModifier;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.select.Select;
import org.apache.ibatis.executor.statement.RoutingStatementHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.session.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
import org.springframework.util.StringUtils;

import java.sql.Connection;
import java.util.*;

/**
 * @author tl
 */
@Intercepts({
        @Signature(method = "prepare", type = StatementHandler.class, args = {Connection.class, Integer.class})
})
@Component
@Slf4j
public class SelectInterceptor implements Interceptor {


    @SuppressWarnings({"unchecked"})
    @Override
    public Object intercept(Invocation invocation) throws Throwable {


        //获取切面的对象，是一个 StatementHandler
        StatementHandler handler = (StatementHandler) invocation.getTarget();
        //访问sql及设置sql，需要访问protected属性，要用MetaObject对象访问
        MetaObject meta = SystemMetaObject.forObject(handler);
        //获取 mappedStatement
//    		MappedStatement mappedStatement = (MappedStatement) meta.getValue("delegate.mappedStatement");
//    		log.info("id: {}", mappedStatement.getId());
        //获取绑定的sql
        BoundSql boundSql = (BoundSql) meta.getValue("delegate.boundSql");
        Configuration configuration = (Configuration) meta.getValue("delegate.configuration");

        log.debug("===============拦截执行sql语句================");
        log.debug("绑定sql：{}, 数据库类型：{}", boundSql.getSql(), configuration.getDatabaseId());

        StopWatch sw = new StopWatch();
        sw.start();
        StatementModifier modifier = null;
        // 执行的Sql数据
        Object parameterObject = ((RoutingStatementHandler) invocation.getTarget()).getParameterHandler().getParameterObject();

        try {
            Statement stmt = CCJSqlParserUtil.parse(boundSql.getSql());
            if (stmt instanceof Select) {
                modifier = new IdentitySelectModifier();
            }
        } catch (Throwable e) {
            e.printStackTrace();
        }
        //执行修改动作
        if (null != modifier) {
            Statement stmt = CCJSqlParserUtil.parse(boundSql.getSql());
            String sql = modifier.modify(boundSql.getSql(),stmt,parameterObject);
            if (!StringUtils.isEmpty(sql)) {
                //重置sql语句和参数
                meta.setValue("delegate.boundSql.sql", sql);

                log.debug("修改后：{}", boundSql.getSql());
            } else {
                log.debug("无需处理");
            }
            List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
            List<ParameterMapping> newParameterMappings = new ArrayList<>();
            for (int i = 0, len = parameterMappings.size(); i < len; i++) {
                    newParameterMappings.add(parameterMappings.get(i));
            }
            for (int i = 0, len = parameterMappings.size(); i < len; i++) {
                newParameterMappings.add(parameterMappings.get(i));
            }
            meta.setValue("delegate.boundSql.parameterMappings", newParameterMappings);



        } else {
            log.debug("无需处理");
        }

        sw.stop();
        log.debug("===============执行时间：{} 秒================", sw.getTotalTimeSeconds());
        log.debug("===============拦截执行sql语句================");

        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        if (target instanceof StatementHandler) {
            return Plugin.wrap(target, this);
        }
        return target;
    }

    @Override
    public void setProperties(Properties properties) {
    }

}
